﻿using FemtoHttpd;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Windows.Forms;

namespace HttpFileServer
{
    public partial class Form1 : Form
    {
        private readonly static string SERVER_SALT = Util.AppGuid;
        private string BASE;
        private int BASE_LEN;
        private int BASE_LEN_1;
        private bool https;

        void InitBase(string password)
        {
            BASE = '/' + Util.Sha1(SERVER_SALT + password + SERVER_SALT).Replace('+', '-').Replace('=', '_') + '/';
            BASE_LEN = BASE.Length;
            BASE_LEN_1 = BASE_LEN - 1;
            //Debug.WriteLine(BASE);
        }

        FemtoHttpServer http;
        Dictionary<string, string> RewriteItems = new Dictionary<string, string>();

        List<string> Dirs = new List<string>();
        string Password => textBox2.Text;

        public Form1()
        {

            InitializeComponent();
            textBox2.KeyDown += (o, e) =>
            {
                if (e.KeyCode == Keys.Enter)
                {
                    if (!http.Run)
                    {
                        btn_StartOrStop_Click(null, null);
                    }
                }
            };
            var cms= listView1.ContextMenuStrip = new ContextMenuStrip();
            cms.Items.Add("清除选中", null, (o, e) => {
                var select = listView1.SelectedItems;
                var count = select.Count;
                listView1.SuspendLayout();
                for(; select.Count>0;)
                {
                    var item = select[0];
                    Dirs.Remove(item.Tag as string);
                    listView1.Items.Remove(item);
                }
                listView1.ResumeLayout();
            });
            http = new FemtoHttpServer();
            http.Get["/"] = (context) =>
            {

                var response = new SimpleResponse(context.Output)
                {
                    Body = Resource1.index_html,
                    ContentType = "text/html"
                };
                try
                {
                    var salt = context.Request.QueryParameters["salt"];
                    var token = context.Request.QueryParameters["token"];
                    var data = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(salt + Password + salt));
                    var sb = new StringBuilder();
                    foreach (byte b in data)
                    {
                        sb.AppendFormat("{0:x2}", b);
                    }
                    if (long.TryParse(salt, out var time) && Math.Abs(time - Util.UtcNow) < 1000 * 30 && sb.ToString() == token.ToLower())
                    {
                        response.Status = new FemtoHttpStatus(307, "Moved Permanently");
                        response.Header["Location"] = BASE;
                        // response.Header["Refresh"] = "1; url=/file/";
                    }
                    else
                    {
                        response.Status = FemtoHttpStatus.BadRequest;
                    }
                    Debug.WriteLine(time + " " + Util.UtcNow);
                }
                catch (Exception e)
                {
                    response.Status = FemtoHttpStatus.Ok;
                }
                return response;
            };
            http.Get["/time"] = (context) =>
            {
                var html = $"<html><body>" +
                $"服务器时间:{DateTime.Now.ToString()}<br />" +
                $"<div id='id1'/>" +
                $"<script>" +
                $"var d =new Date().toLocaleTimeString();" +
                $"document.getElementById('id1').innerHTML='本地时间:'+d;" +
                $"</script></body></html>";

                var response = new SimpleResponse(context.Output)
                {
                    Body = html,
                    ContentType = "text/html"
                };
                response.Status = FemtoHttpStatus.Ok;
                return response;
            };
            http.PreProcessRequest = (context) =>
            {
                if (ProcessRewrite(context, out var response)) return response;//重写Url

                var url = context.Request.Url;
                url = HttpUtility.UrlDecode(url, Encoding.UTF8);
                if (url.IndexOf("..") > 0) return null;

                if (url.StartsWith(BASE))
                {
                    var subPath = url[BASE_LEN_1..];
                    if (subPath.Length < 3)
                    {
                        return new SimpleResponse(context.Output)
                        {
                            Body = Resource1.dir_list_html.Replace("device_count=32", "device_count=" + Dirs.Count),
                            ContentType = "text/html"
                        };
                    }
                    var Dir = Util.tryGet(Dirs, subPath.Substring(1, 2));
                    if (Dir == null) return null;
                    var fileName = Dir + subPath[3..];
                    if (subPath.EndsWith("/"))
                    {
                        var host = context.Request.Header.ContainsKey("Host") ? context.Request.Header["Host"] : "127.0.0.1";
                        return DirListResponse.Create(context.Output, fileName, (https ? "https://" : "http://") + host + url, context.Request.QueryParameters);
                    }
                    else
                    {
                        var range = context.Request.Header.ContainsKey("Range") ? context.Request.Header["Range"] : null;
                        if (range != null)
                        {
                            return RangeFileResponse.Create(context.Output, fileName, range);
                        }
                        else
                        {
                            return FullFileResponse.Create(context.Output, fileName);
                        }
                    }
                }

                else if (url.StartsWith("/public/"))
                {
                    var subPath = url["/public/".Length..];
                    var fileName = subPath;
                    return FullFileResponse.Create(context.Output, "public/" + fileName);
                }

                return null;
            };
            http.NotFound = (context) =>
            {
                var r = new SimpleResponse(context.Output) { Status = FemtoHttpStatus.NotFound, Body = "404,Not Found!……" };
                r.Header["Refresh"] = "2; url=/";
                return r;
            };
            LoadRewrite();
        }
        private bool ProcessRewrite(FemtoHttpContext context, out FemtoHttpResponse response)
        {
            var url = context.Request.Url;
            if (RewriteItems.TryGetValue(url, out var newUrl))
            {
                response = http.GetRewriteResponse(context, newUrl);
                return true;
            }
            response = null;
            return false;
        }
        private void RegisterUrl(string fileName)
        {
            var saveName = Path.GetFileNameWithoutExtension(fileName) + new Random().Next(0, int.MaxValue) + Path.GetExtension(fileName);
            if (Util.FileCopy(fileName, "public/" + saveName))
            {
                var id = Util.CreateID();
                var saveLine = "/r/" + id + "\t" + "/public/" + saveName;
                File.AppendAllText("rewrite.dat", saveLine + "\r\n");
                LoadRewrite();
                MessageBox.Show("/r/" + id);
            }

        }
        private void LoadRewrite()
        {
            string rf = "rewrite.dat";
            if (new FileInfo(rf).Exists)
            {
                var lines = File.ReadAllLines(rf);
                RewriteItems.Clear();
                if (lines?.Length > 0)
                    foreach (var line in lines)
                    {
                        //Trace.WriteLine((int)'\t');
                        var ss = line.Split('\t');
                        //Trace.WriteLine(ss.Length);
                        var u = string.Format(ss[0], BASE);
                        var nu = string.Format(ss[1], BASE);
                        RewriteItems.Add(u, nu);
                    }
            }
        }
        private void btn_StartOrStop_Click(object sender, EventArgs e)
        {
            if (!http.Run)
            {
                InitBase(Password);
                if (!int.TryParse(tb_port.Text, out var port)) return;
                if (https = cb_https.Checked)
                {
                    http.Start(
                        port
                        , new SSLManager("cert.config")
                        );
                }
                else
                {
                    http.Start(port);
                }
                btn_StartOrStop.Text = "停止";
                textBox2.Enabled = false;
                tb_port.Enabled = false;
            }
            else
            {
                if (http.Run)
                {
                    http.Stop();
                }
                btn_StartOrStop.Text = "开始";
                textBox2.Enabled = true;
                tb_port.Enabled = true;
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            if (fbd.ShowDialog() == DialogResult.OK)
            {
                var item=listView1.Items.Add(fbd.SelectedPath);
                item.Tag=fbd.SelectedPath;
                Dirs.Add(fbd.SelectedPath);
            }
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            linkLabel1.Text = "服务器IP：" + ClientIPGeter.GetLocalIp();
        }

        private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                RegisterUrl(ofd.FileName);
            }
        }

        private void linkLabel3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Util.AppGuid = new Random().Next().ToString();
            MessageBox.Show("请重启应用！");
        }

        private void cb_https_CheckedChanged(object sender, EventArgs e)
        {
            tb_port.Text = cb_https.Checked ? "443" : "80";
        }
    }
}